//
//  AKLocalServer.m
//  AKLocalServer
//
//  Created by Yun Qing Teng on 11/12/06.
//  Copyright 2006 __MyCompanyName__. All rights reserved.
//

#import "AKLocalServer.h"

#import <sys/socket.h>
#import <netinet/in.h>
#import <unistd.h>
#import <string.h>

@implementation AKLocalServer
- (void)publishServiceWithName:(NSString *)aName {
	int result;
	
    if(!listeningSocket) {
		
        // Here, create the socket from traditional BSD socket calls, and then set up an NSFileHandle with
        //that to listen for incoming connections.
        
        struct sockaddr_in serverAddress;
        int namelen = sizeof(serverAddress);
		
        // In order to use NSFileHandle's acceptConnectionInBackgroundAndNotify method, we need to create a
        // file descriptor that is itself a socket, bind that socket, and then set it up for listening. At this
        // point, it's ready to be handed off to acceptConnectionInBackgroundAndNotify.
        if((listeningSocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) {
            memset(&serverAddress, 0, sizeof(serverAddress));
            serverAddress.sin_family = AF_INET;
            serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
            serverAddress.sin_port = 0;
			
            // Allow the kernel to choose a random port number by passing in 0 for the port.
            if (bind(listeningSocket, (struct sockaddr *)&serverAddress, namelen) < 0) {
                close (listeningSocket);
                return;
            }
			
            // Find out what port number was chosen.
            if (getsockname(listeningSocket, (struct sockaddr *)&serverAddress, &namelen) < 0) {
                close(listeningSocket);
                return;
            }
			
            chosenPort = ntohs(serverAddress.sin_port);
			
            // Once we're here, we know bind must have returned, so we can start the listen
			result = listen(listeningSocket, 1);
            //if(listen(fdForListening, 1) == 0) {
            //    listeningSocket = [[NSFileHandle alloc] initWithFileDescriptor:fdForListening closeOnDealloc:YES];
            //}
        }
    }
	
    if(!netService) {
        // lazily instantiate the NSNetService object that will advertise on our behalf.  Passing in "" for the domain causes the service
        // to be registered in the default registration domain, which will currently always be "local"
        netService = [[NSNetService alloc] initWithDomain:@"" type:@"_ak._tcp." name:aName port:chosenPort];
        [netService setDelegate:self];
    }
	
    if(netService && result != -1) {
		//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionReceived:) name:NSFileHandleConnectionAcceptedNotification object:listeningSocket];
		//[listeningSocket acceptConnectionInBackgroundAndNotify];
		[NSThread detachNewThreadSelector:@selector(acceptConnection:) toTarget:self withObject:nil];
		[netService publish];
	}
}

- (void)startANewTurn {
}

- (void)sendMessage:(NSString *)aMessage {
	//NSLog(@"abc");
	//NSDictionary *dic = [[NSMutableDictionary alloc] init];
	//[dic setValue:@"aaa" forKey:@"message"];
    //NSData * representationToSend = [NSKeyedArchiver archivedDataWithRootObject:dic];
    //[netService setTXTRecordData:representationToSend];
	//[listeningSocket acceptConnectionInBackgroundAndNotify];
    //[listeningSocket writeData:representationToSend];
    //[listeningSocket closeFile];	
}

- (void)sendNewMoveWithChessIndex:(unsigned)aIndex step:(unsigned)newStep {
}

- (void)setDelegate:(id)aDelegate {
	[aDelegate retain];
	[delegate release];
	
	delegate = aDelegate;
}

- (void)netServiceWillPublish:(NSNetService *)sender  {
	if ([delegate respondsToSelector:@selector(serviceDidPublishedWithName:)]) {
		[delegate serviceDidPublishedWithName:[sender name]];
	}
}

- (void)acceptConnection:(id)anObject {
	struct sockaddr_in address;
	socklen_t addressLength = sizeof(address);
	int  result, fd;
	
	result = accept(listeningSocket, (struct sockaddr *)&address, &addressLength);
	
	if (result == -1) {
		NSLog(@"error at listenging");
		return;
	}
	else {
		NSLog(@"connect");
	}

	
	fd = result;
	
	//set to non-blocking
	result = fcntl (fd, F_SETFL, O_NONBLOCK);
	if (result == -1) {
		NSLog(@"error at listenging");
		return;
	}
	
	user.fd = fd;
	user.bytesRead = 0;
	FD_ZERO(&userfd);
	FD_SET(fd, &userfd);
		
	[NSThread detachNewThreadSelector:@selector(readData:) toTarget:self withObject:nil];
	
	return;
}

- (void)readData:(id)anObject {
	int result;
	int i;
	unichar *scan, messageBuffer[MAX_BUFFER_SIZE + 1];
	
	while(1) {
		result = select(user.fd + 1 , &userfd, NULL, NULL, NULL);
	
		NSLog(@"start read");
		if (result == -1) {
			NSLog(@"error happen");
			return;
		}
	
		result = read(user.fd, user.buffer , READ_BUFFER_SIZE - user.bytesRead);
		
		if (result == 0) {
			NSLog(@"other side close");
			return;
		}
		else if (result == -1) {
			NSLog(@"read error");
			return;
		}
		else {
			user.bytesRead += result;
		}

		NSLog(@"%d", result);
		
		NSString *tempString = [[NSString alloc] initWithCharacters:user.buffer length:(result / sizeof(unichar))];
		NSLog(@"%@", tempString);	
		
		scan = user.buffer;
		
		while (user.bytesRead > 0) {
			if (user.currentMessageSize == 0) {
				//start new message
				user.currentMessageSize = (unsigned char)*scan++;
				NSLog(@"%d", user.currentMessageSize);
				user.bytesRead--;
			}
			
			if (user.bytesRead >= user.currentMessageSize) {
				memcpy(messageBuffer, scan, user.currentMessageSize);
				messageBuffer[user.currentMessageSize] = '\0';
				user.bytesRead -= user.currentMessageSize;
				scan += user.currentMessageSize;
				
				// slide the rest of the data over;
				memmove(user.buffer, scan, user.bytesRead);
				scan = user.buffer;
				
				for (i = 0; i < ( user.currentMessageSize / sizeof(unichar)); i++) {
					NSLog(@"-------------%c", messageBuffer[i]);
				}
				
				user.currentMessageSize = 0;
			}
			// done with this message
			else {
				break;
			}

		}
	}
	
	return;
}
@end
